#include "gpio.h"
typedef volatile uint32_t io_rw_32;
#define _REG_(x)

typedef struct
{
	io_rw_32 accum[2];
	io_rw_32 base[3];
	io_ro_32 pop[3];
	io_ro_32 peek[3];
	io_rw_32 ctrl[2];
	io_rw_32 add_raw[2];
	io_wo_32 base01;
} interp_hw_t;

typedef struct
{
	io_ro_32 cpuid;
	io_ro_32 gpio_in;
	io_ro_32 gpio_hi_in;
	uint32_t _pad0;
	io_rw_32 gpio_out;
	io_rw_32 gpio_hi_out;
	io_wo_32 gpio_set;
	io_wo_32 gpio_hi_set;
	io_wo_32 gpio_clr;
	io_wo_32 gpio_hi_clr;
	io_wo_32 gpio_togl;
	io_wo_32 gpio_hi_togl;
	io_rw_32 gpio_oe;
	io_rw_32 gpio_hi_oe;
	io_wo_32 gpio_oe_set;
	io_wo_32 gpio_hi_oe_set;
	io_wo_32 gpio_oe_clr;
	io_wo_32 gpio_hi_oe_clr;
	io_wo_32 gpio_oe_togl;
	io_wo_32 gpio_hi_oe_togl;
	io_rw_32 fifo_st;
	io_wo_32 fifo_wr;
	io_ro_32 fifo_rd;
	io_ro_32 spinlock_st;
	uint32_t _pad1[8];
	interp_hw_t interp[2];
	io_rw_32 spinlock[32];
	io_rw_32 doorbell_out_set;
	io_rw_32 doorbell_out_clr;
	io_rw_32 doorbell_in_set;
	io_rw_32 doorbell_in_clr;
	io_rw_32 peri_nonsec;
	uint32_t _pad2[3];
	io_rw_32 riscv_softirq;
	io_rw_32 mtime_ctrl;
	uint32_t _pad3[2];
	io_rw_32 mtime;
	io_rw_32 mtimeh;
	io_rw_32 mtimecmp;
	io_rw_32 mtimecmph;
	io_rw_32 tmds_ctrl;
	io_wo_32 tmds_wdata;
	io_ro_32 tmds_peek_single;
	io_ro_32 tmds_pop_single;
	io_ro_32 tmds_peek_double_l0;
	io_ro_32 tmds_pop_double_l0;
	io_ro_32 tmds_peek_double_l1;
	io_ro_32 tmds_pop_double_l1;
	io_ro_32 tmds_peek_double_l2;
	io_ro_32 tmds_pop_double_l2;
} sio_hw_t;

typedef struct
{

	io_ro_32 status;
	io_rw_32 ctrl;
} io_bank0_status_ctrl_hw_t;

typedef struct
{
	io_rw_32 inte[6];
	io_rw_32 intf[6];
	io_ro_32 ints[6];
} io_bank0_irq_ctrl_hw_t;

/// \tag::io_bank0_hw[]
typedef struct
{
	io_bank0_status_ctrl_hw_t io[48];
	uint32_t _pad0[32];
	io_ro_32 irqsummary_proc0_secure[2];
	io_ro_32 irqsummary_proc0_nonsecure[2];
	io_ro_32 irqsummary_proc1_secure[2];
	io_ro_32 irqsummary_proc1_nonsecure[2];
	io_ro_32 irqsummary_dormant_wake_secure[2];
	io_ro_32 irqsummary_dormant_wake_nonsecure[2];
	io_rw_32 intr[6];
	union
	{
		struct
		{
			io_bank0_irq_ctrl_hw_t proc0_irq_ctrl;
			io_bank0_irq_ctrl_hw_t proc1_irq_ctrl;
			io_bank0_irq_ctrl_hw_t dormant_wake_irq_ctrl;
		};
		io_bank0_irq_ctrl_hw_t irq_ctrl[3];
	};
} io_bank0_hw_t;
/// \end::io_bank0_hw[]

#define io_bank0_hw ((io_bank0_hw_t *)IO_BANK0_BASE)
// static_assert(sizeof (io_bank0_hw_t) == 0x0320, "");

typedef struct
{
	io_rw_32 voltage_select;
	io_rw_32 io[48];
} pads_bank0_hw_t;

#define pads_bank0_hw ((pads_bank0_hw_t *)PADS_BANK0_BASE)
// static_assert(sizeof (pads_bank0_hw_t) == 0x00c4, "");
#define sio_hw ((sio_hw_t *)SIO_BASE)
#define sio_ns_hw ((sio_hw_t *)SIO_NONSEC_BASE)
// static_assert(sizeof (sio_hw_t) == 0x01e8, "");

#define io_bank0_hw ((io_bank0_hw_t *)IO_BANK0_BASE)
// #define io io_bank0_hw
#define pads_bank0_hw ((pads_bank0_hw_t *)PADS_BANK0_BASE)
// #define pads pads_bank0_hw

// 0x00000080 [7]     OD           (0) Output disable
// 0x00000040 [6]     IE           (1) Input enable
// 0x00000030 [5:4]   DRIVE        (0x1) Drive strength
// 0x00000008 [3]     PUE          (0) Pull up enable
// 0x00000004 [2]     PDE          (1) Pull down enable
// 0x00000002 [1]     SCHMITT      (1) Enable schmitt trigger
// 0x00000001 [0]     SLEWFAST     (0) Slew rate control
#define PADS_GPIO_OD (1 << 7)
#define PADS_GPIO_IE (1 << 6)
#define PADS_GPIO_PUE (1 << 3)
#define PADS_GPIO_PDE (1 << 2)

void gpio_init(__IO uint32_t gpio, __IO uint32_t func)
{
	io_bank0_hw->io[gpio].ctrl = func;
	pads_bank0_hw->io[gpio] = PADS_GPIO_IE | PADS_GPIO_OD;
	sio_hw->gpio_oe_clr = 1 << gpio;
}

void gpio_dir(__IO uint32_t gpio, __IO uint32_t out)
{
	if (out)
	{
		sio_hw->gpio_oe_set = 1 << gpio;
		pads_bank0_hw->io[gpio] = 0;
	}
	else
	{
		sio_hw->gpio_oe_clr = 1 << gpio;
		pads_bank0_hw->io[gpio] = PADS_GPIO_IE | PADS_GPIO_OD;
	}
}

void gpio_set(__IO uint32_t gpio, __IO uint32_t value)
{
	if (value)
		sio_hw->gpio_set = 1 << gpio;
	else
		sio_hw->gpio_clr = 1 << gpio;
}

__IO uint32_t gpio_get(__IO uint32_t gpio)
{
	return sio_hw->gpio_in & (1 << gpio);
}

void gpio_deinit(__IO uint32_t gpio)
{
	pads_bank0_hw->io[gpio] = PADS_GPIO_IE | PADS_GPIO_OD;
	sio_hw->gpio_oe_clr = 1 << gpio;
}

void gpio_pullup(__IO uint32_t gpio, __IO uint32_t enable)
{
	__IO uint32_t reg = pads_bank0_hw->io[gpio];

	if (enable)
	{
		reg |= PADS_GPIO_PUE;
	}
	else
	{
		reg &= (~PADS_GPIO_PUE);
	}
	pads_bank0_hw->io[gpio] = reg;
}

void gpio_pulldown(__IO uint32_t gpio, __IO uint32_t enable)
{
	__IO uint32_t reg = pads_bank0_hw->io[gpio];

	if (enable)
	{
		reg |= PADS_GPIO_PDE;
	}
	else
	{
		reg &= (~PADS_GPIO_PDE);
	}
	pads_bank0_hw->io[gpio] = reg;
}
